/*!
 * 
 */

(function (w) {
    var reference = "EventBus";
    var oldReference = w[reference];
    this.global = "global";
    this._default = "__default__";
    this.listeners = {};
    this.sequenceIndex = 0;
    this.currentSpace = "global";
    var _this = this;

    function initArg(arg, defaultValue) {
        if (arg == null || typeof(arg) == "undefined") {
            return defaultValue;
        }
        return arg;
    }

    function initObject(object, property, defaultValue) {
        if (object.hasOwnProperty(property)) {
            return;
        }
        object[property] = defaultValue;
    }

    this.getCurrentSpace = function () {
        return this.currentSpace;
    };

    this.changeSpace = function (space, clearOtherSpace) {
        this.currentSpace = space;
        if (typeof(clearOtherSpace) != "undefined" && clearOtherSpace == true) {
            for (var key in this.listeners) {
                if (key == this.global || key.substring(0, 1) == "_") {
                    continue;
                }
                if (key == space) {
                    continue;
                }
                delete this.listeners[key];
            }
        }
    };

    this.globalTrigger = function (signal, args, event) {
        this.doTrigger(signal, args, this.global, event);
    };

    this.trigger = function (signal, args, event) {
        this.doTrigger(signal, args, null, event);
    };

    this.spaceTrigger = function (signal, args, space, event) {
        this.doTrigger(signal, args, space, event);
    };

    this.doTrigger = function (signal, args, space, event) {
        space = initArg(space, this.currentSpace);
        if (space == "*") {
            var names = _this.spaceNames();
            for (var i = 0; i < names.length; i++) {
                var name = names[i];
                _this.triggerSpace(signal, name, args, event);
            }
        } else {
            _this.triggerSpace(signal, space, args, event);
        }
    };

    this.triggerSpace = function (signal, space, args, event) {
        var signals = this.signalNames(space);
        var founded = false;
        for (var i = 0; i < signals.length; i++) {
            var registeredSignal = signals[i];
            if (signal == registeredSignal || WildUtils.match(signal, registeredSignal)) {
                var list = this.getListenerList(space, registeredSignal).toArray();
                _this.triggerEvent(list, args, event);
                founded = true;
            } else {
                // console.log(registeredSignal, " not match", signal);
            }
        }
        if (!founded) {
            console.warn("信号: " + signal + ",空间:" + space + " 没有对象监听");
        }
    };

    this.triggerEvent = function (list, args, event) {
        for (var i = 0; i < list.length; i++) {
            var listener = list[i];
            if (listener.isExpired()) {
                console.log(listener.getName() + " is expired");
                continue;
            }
            var func = listener.callback;
            var result = func.call(func, args, event);
            if (result == false) {
                console.warn("listener for ", listener.signal, "at", listener.space, " by ", listener.subscriberId, "return false,interrupt chains");
                break;
            }
            listener.incrementInvokeTimes();
        }
    };

    this.globalListen = function (signal, callback, subscriberId, priority) {
        this.doListen(signal, callback, subscriberId, priority, this.global, 0);
    };

    this.globalListenOnce = function (signal, callback, subscriberId, priority) {
        this.doListen(signal, callback, subscriberId, priority, this.global, 1);
    };

    this.listen = function (signal, callback, subscriberId, priority, space) {
        this.doListen(signal, callback, subscriberId, priority, space, 0);
    };

    this.listenOnce = function (signal, callback, subscriberId, priority, space) {
        this.doListen(signal, callback, subscriberId, priority, space, 1);
    };

    this.doListen = function (signal, callback, subscriberId, priority, space, invokeTimes) {
        addListener(signal, callback, subscriberId, priority, space, invokeTimes);
    };

    this.addListener = function (signal, callback, subscriberId, priority, space, invokeTimes) {
        var listener = new Listener(signal, callback, subscriberId, priority, space, invokeTimes);
        var list = this.getListenerList(listener.space, listener.signal);
        var existsIndex = -1;
        var matrix = list.toArray();
        for (var i = 0; i < matrix.length; i++) {
            var item = list.get(i);
            if (item.subscriberId == subscriberId) {
                existsIndex = i;
                break;
            }
        }
        if (existsIndex >= 0) {
            console.warn("listener for ", signal, " by ", subscriberId, " already exists");
            list.set(existsIndex, listener);
        } else {
            list.add(listener);
        }
        // this.debugMe();
    };

    this.removeListen = function (signal, space, subscriberId) {
        var linkedList = this.getListenerList(space, signal);
        var list = linkedList.toArray();
        for (var i = list.length - 1; i >= 0; i--) {
            var listener = list[i];
            if (listener.subscriberId == subscriberId) {
                linkedList.remove(i);
            }
        }
    };

    //退订某个对象的所有监听
    this.removeMines = function (subscriberId) {
        for (var key in this.listeners) {
            for (var signal in this.listeners[key]) {
                var list = this.getListenerList(key, signal);
                for (var i = list.length - 1; i >= 0; i--) {
                    var listener = list[i];
                    if (listener.subscriberId == subscriberId) {
                        linkedList.remove(i);
                    }
                }
            }
        }
        // this.debugMe();
    };

    this.viewList = function (list) {
        var matrix = list.toArray();
        for (var i = 0; i < list.length; i++) {
            var item = list[i];
            console.log(item.signal, item.priority, item.sequence);
        }
    };

    this.getListenerList = function (space, signal) {
        // console.log("listeners", JSON.stringify(this.listeners));
        initObject(this.listeners, space, {});
        if (!this.listeners[space].hasOwnProperty(signal)) {
            var list = new Collection.SortedList();
            list.compare = function (a, b) {
                if (a.priority < b.priority) {
                    return 1;
                }
                if (a.priority == b.priority) {
                    if (a.sequence < b.sequence) {
                        return 1;
                    }
                }
                return -1;
            };
            this.listeners[space][signal] = list;
        }
        return this.listeners[space][signal];
    };

    this.spaceNames = function () {
        var result = [];
        for (var key in this.listeners) {
            result.push(key);
        }
        return result;
    };

    this.signalNames = function (space) {
        var result = [];
        if (this.listeners.hasOwnProperty(space)) {
            for (var key in this.listeners[space]) {
                result.push(key);
            }
        }
        return result;
    };

    this.debugMe = function () {
        console.log("--------------------------event bus info----------------------------");
        console.log(this.toString());
        console.log("--------------------------------------------------------------------");
    };

    this.toString = function () {
        var s = "";
        for (var key in this.listeners) {
            s += "\r\n空间:" + key;
            for (var signal in this.listeners[key]) {
                s += "\r\n----信号:" + signal;
                var matrix = this.getListenerList(key, signal);
                var list = matrix.toArray();
                // console.log(key, signal, " list ", list);
                for (var i = 0; i < list.length; i++) {
                    var listener = list[i];
                    s += "\r\n--------优先级:" + listener.priority;
                    s += ",最大调用次数:" + listener.invokeTimes;
                    s += ",订阅者:" + listener.subscriberId;
                    s += ",序号:" + listener.sequence;
                    s += ",执行次数:" + listener.triggerTimes;
                    s += ",函数名:" + listener.callback.getName();
                }
            }
        }
        return s;
    };

    function Listener(signal, callback, subscriberId, priority, space, invokeTimes) {
        subscriberId = initArg(subscriberId, EventBus._default);
        priority = initArg(priority, 100);
        space = initArg(space, EventBus.currentSpace);
        invokeTimes = initArg(invokeTimes, 0);
        this.signal = signal;
        this.callback = callback;
        this.subscriberId = subscriberId;
        this.priority = priority;
        this.invokeTimes = invokeTimes;
        this.space = space;
        this.sequence = _this.sequenceIndex++;
        this.triggerTimes = 0;
        this.isExpired = function () {
            if (this.invokeTimes == 0) {
                return false;
            }
            return this.triggerTimes >= this.invokeTimes;
        };
        this.incrementInvokeTimes = function () {
            this.triggerTimes++;
        };
        this.getName = function () {
            return "[signal:" + this.signal + ",subscriberId:" + this.subscriberId + ",space:" + this.space + ",priority:" + priority + "]";
        };
    };

    this.isDefined = function (object) {
        return typeof(object) != "undefined" && object != null;
    };

    this.log = function (info) {
        console.log(info);
    };


    w[reference] = _this;

})(window);

Function.prototype.getName = function () {
    return this.name || this.toString().match(/function\s*([^(]*)\(/)[1]
};